﻿// OraHome.cs
// Aleksei Vassiljev
// {4DC17B88-5237-4e95-A60D-72988F554D4F}

using System;
using System.IO;
using System.Linq;
using Microsoft.Win32;
using System.Diagnostics;


namespace OracleTools.Core
{
    public class OraHome : IEquatable<OraHome>
    {
        #region Public Properties

        public string Name { get; private set; }
        /// <summary>
        /// Registry key string
        /// </summary>
        public string Key { get; private set; }
        public OraVersion Version { get; set; }

        public string BundleName { get; private set; }
        public string GroupName { get; private set; }
        public string NlsLang { get; private set; }
        /// <summary>
        /// Full path to %oracle_home% directory
        /// </summary>
        public string Path { get; private set; }
        /// <summary>
        /// Full path to %oracle_home%/bin directory. Does not exists in instant client
        /// </summary>
        public string BinPath { get; private set; }
        /// <summary>
        /// Full path to %oracle_home%\oledb\mesg directory
        /// </summary>
        public string OLEDB { get; private set; }
        /// <summary>
        /// Full path to %oracle_home%\oo4o\mesg directory
        /// </summary>
        public string OO4O { get; private set; }
        /// <summary>
        /// Full path to %oracle_home%/dbs directory
        /// </summary>
        public string SqlPath { get; private set; }

        public bool IsCurrent { get; set; }
        /// <summary>
        /// Is it 32bit version on 64bit platform
        /// </summary>
        public bool X32ClientOnX64OS { get; private set; }

        /// <summary>
        /// Is 64bit client
        /// </summary>
        public bool X64 { get; private set; }

        /// <summary>
        /// Full path to bin/oipXX.dll file
        /// </summary>
        public string OipDllPath { get; private set; }
        /// <summary>
        /// Full path to bin/oipXX.tlb file - Oracle InProc Server 5.0 Type Library
        /// </summary>
        public string OipTlbPath { get; private set; }
        /// <summary>
        /// Full path to bin/oradc.ocx file - Oracle Data Control
        /// </summary>
        public string OraDcOcxPath { get; private set; }
        /// <summary>
        /// Full path to OraOLEDBXX.dll file - OraOLEDB 1.0 Type Library
        /// </summary>
        public string OraOledbDllPath { get; private set; }


        /// <summary>
        /// Full path to the tnsnames.ora file
        /// </summary>
        public string TnsOraFilePath { get; private set; }
        /// <summary>
        /// Full path to the sqlnet.ora file
        /// </summary>
        public string SqlNetOraFilePath { get; private set; }
        /// <summary>
        /// Full path to the ldap.ora file
        /// </summary>
        public string LdapOraFilePath { get; private set; }

        #endregion Public Properties

        private OraHome() { }

        public static OraHome createInstance(RegistryKey key)
        {
            OraHome home = null;

            try
            {
                //bool bWow64 = key.ToString().ToLower().Contains("wow6432node");

                home = new OraHome();
                home.Version = new OraVersion();
                home.IsCurrent = false;
                string[] arrFileSearchResult;

                home.Name = key.GetValue(RegKeyEx.ORACLE_HOME_NAME, "") as string;
                home.Path = key.GetValue(RegKeyEx.ORACLE_HOME, "") as string;

                if (Directory.Exists(home.Path + "\\bin"))
                    home.BinPath = home.Path + "\\bin";
                else
                    return null;

                home.Key = key.Name.Substring(key.Name.IndexOf("\\") + 1);

                if (Environment.Is64BitOperatingSystem)
                {
                    // x32 client on x64 os will have oracle home key in HKLM\Software\Wow6432Node\Oracle
                    if (key.ToString().ToLower().Contains("wow6432node"))
                    {
                        home.X32ClientOnX64OS = true;
                        home.X64 = false;
                    }
                    else
                    {
                        home.X32ClientOnX64OS = false;
                        home.X64 = true;
                    }
                }
                else
                {
                    home.X32ClientOnX64OS = false;
                    home.X64 = false;
                }

                // Find file oracoreXX.dll to find out oracle version
                // (maybe there is some more correct path to determine version?)
                arrFileSearchResult = Directory.GetFiles(home.BinPath, "oracore*.dll", SearchOption.TopDirectoryOnly);
                if (arrFileSearchResult.Length > 0)
                {
                    string sName = arrFileSearchResult[0];
                    sName = sName.Substring(sName.LastIndexOf("\\") + 1);
                    string sVersion = sName.Substring(7, sName.Length - 11);
                    int nVersion;
                    if (int.TryParse(sVersion, out nVersion))
                    {
                        home.Version = new OraVersion();
                        home.Version.Major = nVersion;
                    }
                }

                home.BundleName = key.GetValue(RegKeyEx.ORACLE_BUNDLE_NAME, "") as string;
                home.GroupName = key.GetValue(RegKeyEx.ORACLE_GROUP_NAME, "") as string;
                home.NlsLang = key.GetValue(RegKeyEx.NLS_LANG, "") as string;

                home.OLEDB = key.GetValue(RegKeyEx.OLEDB, "") as string;
                if (home.OLEDB == "")
                {
                    if (Directory.Exists(home.Path + "\\oledb\\mesg"))
                        home.OLEDB = home.Path + "\\oledb\\mesg";
                }

                home.OO4O = key.GetValue(RegKeyEx.OO4O, "") as string;
                if (home.OO4O == "")
                {
                    if (Directory.Exists(home.Path + "\\oo4o\\mesg"))
                        home.OO4O = home.Path + "\\oo4o\\mesg";
                }

                home.SqlPath = key.GetValue("SQLPATH", "") as string;

                // Find oipXX.dll file in orahome's bin directory
                // If oipXX.dll exists - then oipXX.tlb must be there also
 
                arrFileSearchResult = Directory.GetFiles(home.BinPath, "oip*.dll", SearchOption.TopDirectoryOnly);
                if (arrFileSearchResult.Length > 0)
                {
                    home.OipDllPath = arrFileSearchResult[0];
                    home.OipTlbPath = arrFileSearchResult[0].Remove(arrFileSearchResult[0].Length - 3) + "tlb";
                }
                else
                {
                    home.OipDllPath = "";
                    home.OipTlbPath = "";
                }
                

                // Find OraOLEDBXX.dll file in orahome's bin directory
                arrFileSearchResult = Directory.GetFiles(home.BinPath, "OraOLEDB*.dll", SearchOption.TopDirectoryOnly);
                if (arrFileSearchResult.Length > 0)
                {
                    home.OraOledbDllPath = arrFileSearchResult.OrderBy(a => a.Length).First();
                }
                else
                {
                    home.OraOledbDllPath = "";
                }


                // Find oradc.ocx file
                arrFileSearchResult = Directory.GetFiles(home.BinPath, "oradc.ocx", SearchOption.TopDirectoryOnly);
                if (arrFileSearchResult.Length > 0)
                    home.OraDcOcxPath = arrFileSearchResult[0];
                else
                    home.OraDcOcxPath = "";


                // Find sqlnet.ora file
                home.SqlNetOraFilePath = home.FindFileInAdminFolder("sqlnet.ora");

                // Read the sqlnet.ora file
                // TODO

                // Find ldap.ora file
                home.LdapOraFilePath = home.FindFileInAdminFolder("ldap.ora");

                // Find tnsnames.ora file
                home.TnsOraFilePath = home.FindFileInAdminFolder("tnsnames.ora");
            }
            catch (Exception ex)
            {
                home = null;
                Debug.WriteLine("OraHome.createInstance: {0}", ex.ToString());
            }
            return home;
        }

        /// <summary>
        /// Check if file exists in network\admin or net80\admin.
        /// If exists - then returns file's full path, otherwise returns empty string
        /// </summary>
        /// <param name="psFileName"></param>
        /// <returns></returns>
        private string FindFileInAdminFolder(string psFileName)
        {
            string sPath = this.Path + @"\network\admin\" + psFileName;
            if (!File.Exists(sPath))
            {
                sPath = this.Path + @"\net80\admin\" + psFileName;
                if (!File.Exists(sPath))
                    sPath = "";
            }
            return sPath;
        }
        

        public override string ToString()
        {
            return this.Name;
        }

        public static bool operator == (OraHome home1, OraHome home2)
        {
            
            if (ReferenceEquals(null, home1)) return ReferenceEquals(null, home2);
            return home1.Equals(home2);
        }

        public static bool operator !=(OraHome home1, OraHome home2)
        {
            return !(home1 == home2);
        }


        public bool Equals(OraHome other)
        {
            if (ReferenceEquals(null, other)) return false;
            return this.Path.ToLower().Equals(other.Path.ToLower());
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false; 

            if (!(obj is OraHome))
                return false;
            else
                return Equals(obj as OraHome);   
        }

        public override int GetHashCode()
        {
            return this.Path.ToLower().GetHashCode();
        }
    }
}
